package com.danicsoft.daide.binary;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;

import com.danicsoft.daide.historian.BinaryHistorian;

/**
 * This class handles all communication for a client.
 * 
 * @author Daniel Yule (daniel.yule@gmail.com)
 * @version 1.0
 */
public class ClientConnection extends Connection {

	/**
	 * Construct a new connection that will connect to the server at the given
	 * address on the given port, but do not yet connect.
	 * 
	 * @param ip
	 *        The IP address of the server we are connecting to.
	 * @param port
	 *        The port we are connecting to the server on
	 * @param historian
	 *        The historian we will use to log problems
	 */
	public ClientConnection(InetAddress ip, int port, BinaryHistorian historian) {
		super(ip, port, historian);
	}
	
	/**
	 * Construct a new connection that will connect to the server at the given
	 * address on the given port, but do not yet connect.
	 * 
	 * @param ip
	 *        The IP address of the server we are connecting to.
	 * @param port
	 *        The port we are connecting to the server on
	 */
	public ClientConnection(InetAddress ip, int port) {
		super(ip, port);
	}

	/**
	 * Connects to the server. If the server listens on the specified TCP
	 * address, the handshake procedure will be attempted;
	 * <OL>
	 * <LI> an <code>Initial Message</code> will be sent </LI>
	 * <LI> a <code>Representation Message</code> will be received </LI>
	 * </OL>
	 * 
	 * @throws IOException
	 *         if data could not be sent to the server (Possibly because there
	 *         is no answer at the specified address)
	 */
	@Override
	public void connect(ByteMessageHandler byteHandler) throws IOException {
		super.connect(byteHandler);
		try {
			setState(CONNECTION_STATE.CONNECTING);
			connect_to_socket(new Socket(ip, port));

			// Send the Initial Message
			byte[] im = createInitialMessage();
			send(im);

			// Receive the Representation Message
			waitForConnected();
		}
		catch (DisconnectedException e) {
			throw new IOException("Could not maintain connection");
		}
	}

	@Override
	protected synchronized void handleDiplomaticMessage(byte[] message) {
		if (getState() != CONNECTION_STATE.CONNECTED) {

			sendErrorMessage(RM_NOT_FIRST);

		}

		parseDiplomaticMessage(message);

	}

	@Override
	protected void handleErrorMessage(byte[] message) {

		historian.logConnectionError("Server reports: " + getErrorMessage(message[1]), this);

		severConnection();

	}

	@Override
	protected synchronized void handleInitialMessage(byte[] message) {
		sendErrorMessage(SERVER_IM);
	}

	/**
	 * Handles a <code>Representation Message</code>. Any tokens encoded in
	 * the message body will be added to the Token singleton. Upon correct
	 * decoding, the state will be set to <code>RM_RECEIVED</code>
	 * 
	 * @param message
	 *        The body of the message.
	 */
	@Override
	protected synchronized void handleRepresentationMessage(byte[] message) {

		if (getState() == CONNECTION_STATE.CONNECTED) {
			sendErrorMessage(MULTIPLE_RM);
		}
		setRM(message);
		setState(CONNECTION_STATE.CONNECTED);
	}

}
